#include <linux/fs2.h>
#include <linux/kernel.h>

#include "internal.h"

#define ROMFS_START_SECTOR_ON_SDCARD	206848

static int romfs_blk_read(struct super_block *sb, unsigned long pos,
			  void *buf, size_t buflen)
{
	// struct buffer_head *bh;
	unsigned long offset;
	size_t segment;
	char *sd_buf = (char *)kmalloc(ROMBSIZE);

	/* copy the string up to blocksize bytes at a time */
	while (buflen > 0) {
		offset = pos & (ROMBSIZE - 1);
		segment = min_t(size_t, buflen, ROMBSIZE - offset);
#if 0
		bh = sb_bread(sb, pos >> ROMBSBITS);
		if (!bh)
			return -EIO;
		memcpy(buf, bh->b_data + offset, segment);
		brelse(bh);
#else
		sd_read_sector((unsigned int *)sd_buf, ROMFS_START_SECTOR_ON_SDCARD + (pos >> ROMBSBITS), 1);
		memcpy(buf, sd_buf + offset, segment);
#endif
		buf += segment;
		buflen -= segment;
		pos += segment;
	}

	return 0;
}

static ssize_t romfs_blk_strnlen(struct super_block *sb,
				 unsigned long pos, size_t limit)
{
	struct buffer_head *bh;
	unsigned long offset;
	ssize_t n = 0;
	size_t segment;
	u_char *buf, *p;
	char *sd_buf = (char *)kmalloc(ROMBSIZE);

	/* scan the string up to blocksize bytes at a time */
	while (limit > 0) {
		offset = pos & (ROMBSIZE - 1);
		segment = min_t(size_t, limit, ROMBSIZE - offset);
#if 0
		bh = sb_bread(sb, pos >> ROMBSBITS);
		if (!bh)
			return -EIO;
		buf = bh->b_data + offset;
		p = memchr(buf, 0, segment);
		brelse(bh);
#else
		sd_read_sector((unsigned int *)sd_buf, ROMFS_START_SECTOR_ON_SDCARD + (pos >> ROMBSBITS), 1);
		buf = sd_buf + offset;
		p = memchr(buf, 0, segment);
#endif
		if (p)
			return n + (p - buf);
		limit -= segment;
		pos += segment;
		n += segment;
	}

	return n;
}

static int romfs_blk_strcmp(struct super_block *sb, unsigned long pos,
			    const char *str, size_t size)
{
	struct buffer_head *bh;
	unsigned long offset;
	size_t segment;
	bool matched, terminated = false;
	char *sd_buf = (char *)kmalloc(ROMBSIZE);

	/* compare string up to a block at a time */
	while (size > 0) {
		offset = pos & (ROMBSIZE - 1);
		segment = min_t(size_t, size, ROMBSIZE - offset);
#if 0
		bh = sb_bread(sb, pos >> ROMBSBITS);
		if (!bh)
			return -EIO;
		matched = (memcmp(bh->b_data + offset, str, segment) == 0);
#else
		sd_read_sector((unsigned int *)sd_buf, ROMFS_START_SECTOR_ON_SDCARD + (pos >> ROMBSBITS), 1);
		matched = (memcmp(sd_buf + offset, str, segment) == 0);
#endif

		size -= segment;
		pos += segment;
		str += segment;
		if (matched && size == 0 && offset + segment < ROMBSIZE) {
			// if (!bh->b_data[offset + segment])
			if (!sd_buf[offset + segment])
				terminated = true;
			else
				matched = false;
		}
		// brelse(bh);
		if (!matched)
			return 0;
	}

	if (!terminated) {
		/* the terminating NUL must be on the first byte of the next
		 * block */
		// BUG_ON((pos & (ROMBSIZE - 1)) != 0);
#if 0
		bh = sb_bread(sb, pos >> ROMBSBITS);
		if (!bh)
			return -EIO;
		matched = !bh->b_data[0];
		brelse(bh);
#else
		sd_read_sector((unsigned int *)sd_buf, ROMFS_START_SECTOR_ON_SDCARD + (pos >> ROMBSBITS), 1);
		matched = !sd_buf[0];
#endif
		if (!matched)
			return 0;
	}

	return 1;
}

int romfs_dev_read(struct super_block *sb, unsigned long pos,
		   void *buf, size_t buflen)
{
	// size_t limit;

	// limit = romfs_maxsize(sb);
	// if (pos >= limit)
	// 	return -EIO;
	// if (buflen > limit - pos)
	// 	buflen = limit - pos;

// #ifdef CONFIG_ROMFS_ON_MTD
// 	if (sb->s_mtd)
// 		return romfs_mtd_read(sb, pos, buf, buflen);
// #endif
// #ifdef CONFIG_ROMFS_ON_BLOCK
// 	if (sb->s_bdev)
		return romfs_blk_read(sb, pos, buf, buflen);
// #endif
	// return -EIO;
}

ssize_t romfs_dev_strnlen(struct super_block *sb,
			  unsigned long pos, size_t maxlen)
{
// 	size_t limit;

// 	limit = romfs_maxsize(sb);
// 	if (pos >= limit)
// 		return -EIO;
// 	if (maxlen > limit - pos)
// 		maxlen = limit - pos;

// #ifdef CONFIG_ROMFS_ON_MTD
// 	if (sb->s_mtd)
// 		return romfs_mtd_strnlen(sb, pos, maxlen);
// #endif
// #ifdef CONFIG_ROMFS_ON_BLOCK
// 	if (sb->s_bdev)
		return romfs_blk_strnlen(sb, pos, maxlen);
// #endif
	// return -EIO;
}

int romfs_dev_strcmp(struct super_block *sb, unsigned long pos,
		     const char *str, size_t size)
{
// 	size_t limit;

// 	limit = romfs_maxsize(sb);
// 	if (pos >= limit)
// 		return -EIO;
// 	if (size > ROMFS_MAXFN)
// 		return -ENAMETOOLONG;
// 	if (size + 1 > limit - pos)
// 		return -EIO;

// #ifdef CONFIG_ROMFS_ON_MTD
// 	if (sb->s_mtd)
// 		return romfs_mtd_strcmp(sb, pos, str, size);
// #endif
// #ifdef CONFIG_ROMFS_ON_BLOCK
// 	if (sb->s_bdev)
		return romfs_blk_strcmp(sb, pos, str, size);
// #endif
	// return -EIO;
}
